package cate.game.role.bag;

import cate.common.table.d.GDObj;
import cate.common.table.item.IBagItemTpl;
import cate.common.util.GameResult;
import cate.game.client.msg.MergeMsgSender;
import cate.game.log.GameLoggerFactory;
import cate.game.res.RewardShow;
import cate.game.role.RolePart;
import cate.game.role.Role;
import cate.game.role.bag.item.BaseBagItem;
import cate.game.role.bag.item.msg.ItemAddResp;
import cate.game.role.bag.item.msg.ItemNumResp;
import cate.game.role.bag.item.msg.ItemRemoveResp;
import cate.game.role.system.RoleSystem;
import cate.game.stat.item.StatItemIOEntity;
import cate.game.util.GameKit;
import com.fasterxml.jackson.annotation.JsonIgnore;
import easy.java.dev.note.NoteField;
import lombok.Getter;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.springframework.data.annotation.Transient;

import java.util.*;

public abstract class BagContainer<E extends BaseBagItem> extends RolePart implements IBagContainer<E>{
    @Transient
    @JsonIgnore
    protected transient Logger log;

    @JsonIgnore
    @Transient
    @NoteField("唯一id，为了区别背包使用")
    protected transient String subUid = GameKit.uid();

    /** 当前背包容量 */
    @NoteField("当前背包容量")
    @Getter
    private int capacity;

    @NoteField("扩容次数")
    @Getter
    private int expandTimes;

    @NoteField(value = "数值资源列表")
    private List<E> list;

    public BagContainer(){}

    public BagContainer(Role role) {
        super(role);
        initialize(role);
    }

    @Override
    public void initialize(Role role){
        super.initialize(role);
        log = GameLoggerFactory.getLogger(role.getGame(), this.getClass());
        if(list == null){
            list = new ArrayList<>();
        }
        updateCapacity();
        Iterator<E> iter = getList().iterator();
        while (iter.hasNext()) {
            E e = iter.next();
            if (null == e) {
                iter.remove();
            } else {
                e.initialize(role);
            }
        }
    }

    @Override
    @JsonIgnore
    public String getUid() {
        return subUid;
    }

    @Override
    @JsonIgnore
    public String getBagName() {
        return GDObj.Type.name(getItemType());
    }

    @Override
    public List<E> getList() {
        return this.list;
    }

    public void addCapacity(){
        expandTimes++;
        updateCapacity();
    }

    public void updateCapacity(){
        capacity = calcCapacity();
    }

    protected abstract int calcCapacity();

    @Override
    public long count(int tid) {
        long total = 0;
        for (E item : getList()) {
            if (item.tid == tid) {
                total += item.num;
            }
        }
        return total;
    }

    /** 根据_id获取指定对象 */
    @Override
    public E getItem(String uid) {
        if(StringUtils.isBlank(uid)){
            return null;
        }
        for (E item : getList()) {
            if (StringUtils.equals(uid, item.uid)) {
                return item;
            }
        }
        return null;
    }

    protected int itemSize(){
        return this.getList().size();
    }

    @Override
    public GameResult<Void> addItemCheck(int templateId, long num) {
        GameResult<Void> r = new GameResult<>();
        if (num < 0) {
            return r.fail("非法数据");
        }
        IBagItemTpl tpl = getItemTpl(templateId);
        if (tpl == null) {
            return r.fail("物品不存在");
        }
        GameResult<Void> cir = customItemCheck(templateId, num);
        if(!cir.ok()){
            return r.fail(cir.message);
        }
        int itemSize = itemSize();
        // 剩余的格子够物品数量，那么必然是可以添加的
        long leftGridNum = (this.capacity - itemSize) * (tpl.unique() ? 1 : tpl.getPileMax());
        if (leftGridNum >= num) {
            return r.success();
        } else if (tpl.unique()) {
            return r.fail("背包已满");
        }
        // 接下来获取可用的总数量
        for (int i = 0; i < itemSize; i++) {
            BaseBagItem item = this.getList().get(i);
            if (item.tid != templateId || item.getTpl(role.toPlayBuildContext()).unique() || item.unique()) {
                continue;
            }
            // 该物品格子的剩余可堆叠数量
            leftGridNum += tpl.getPileMax() - item.num;
            if (leftGridNum >= num) {
                return r.success();
            }
        }
        return r.fail("背包已满");
    }

    /** 添加 */
    @Override
    public GameResult<Void> addItem(E item, MergeMsgSender sender) {
        GameResult<Void> r = addItemCheck(item.tid, item.num);
        if (!r.ok()) {
            return r;
        }
        this.getList().add(item);
        ItemAddResp addMsg = createItemAddMsg();
        addMsg.add(item);
        MergeMsgSender.handle(sender, addMsg, role);
        onItemCreated(item);
        onItemIncreased(item.tid, item.num);
        return r.success();
    }

    /** 添加 */
    @Override
    public GameResult<Void> addItems(List<E> items, MergeMsgSender sender) {
        GameResult<Void> r = new GameResult<>();
        if (gridEmpty() < items.size()) {
            return r.fail("背包已满");
        }
        this.getList().addAll(items);
        ItemAddResp addMsg = createItemAddMsg();
        for (E item : items) {
            addMsg.add(item);
        }
        MergeMsgSender.handle(sender, addMsg, this.role);
        for (E item : items) {
            onItemCreated(item);
            onItemIncreased(item.tid, item.num);
        }
        return r.success();
    }

    /** 添加 */
    @Override
    public GameResult<Void> addItem(int tid, long num, MergeMsgSender sender) {
        GameResult<Void> r = addItemCheck(tid, num);
        if (!r.ok()) {
            return r;
        }
        ItemNumResp numMsg = new ItemNumResp(getItemType()); // 物品数量变更的消息
        IBagItemTpl tpl = getItemTpl(tid);
        long leftToAdd = num;
        for (E item : this.getList()) {
            if (leftToAdd <= 0) {
                break;
            }
            if (item.tid != tpl.getTemplateId() || item.getTpl(role.toPlayBuildContext()).unique() || item.unique()) {
                continue;
            }
            // 该物品格子的剩余可堆叠数量
            long itemLeftNum = tpl.getPileMax() - item.num;
            if (itemLeftNum > 0) {
                long toAdd = Math.min(itemLeftNum, leftToAdd);
                item.addNum(toAdd);
                leftToAdd -= toAdd;
                numMsg.addChange(item.uid, item.num);
            }
        }
        if (!numMsg.changes.isEmpty()) {
            MergeMsgSender.handle(sender, numMsg, this.role);
        }
        // 如果还有没堆叠完的，就要创建新的物品项
        if (leftToAdd > 0) {
            List<E> items = new ArrayList<>();
            ItemAddResp addMsg = createItemAddMsg();
            int newGridNum = (int) Math.ceil((double) leftToAdd / tpl.getPileMax());
            for (int i = 0; i < newGridNum; i++) {
                long toAdd = Math.min(leftToAdd, tpl.getPileMax());
                leftToAdd -= toAdd;
                E item = createItem(tid, toAdd);
                if (item != null) {
                    this.getList().add(item);
                    items.add(item);
                    addMsg.add(item);
                }
            }
            MergeMsgSender.handle(sender, addMsg, this.role);
            for (E e : items) {
                onItemCreated(e);
            }
        }
        // 统计需要放在最后
        onItemIncreased(tid, num);
        return r;
    }

    /**
     * 移除了，又没完全移除，比如穿戴或放到仓库里，不触发移除事件
     */
    @Override
    public GameResult<RewardShow> notReallyRemove(String uid, MergeMsgSender sender) {
        return removeItem(uid, sender, false);
    }

    @Override
    public GameResult<RewardShow> notReallyRemove(int tid, long num, MergeMsgSender sender) {
        return removeItem(tid, num, sender, false);
    }

    /** 根据唯一id来删除 */
    @Override
    public GameResult<RewardShow> notReallyRemoveItemByUid(
            String uid, long num, MergeMsgSender sender) {
        GameResult<RewardShow> r = new GameResult<>();
        BaseBagItem item = this.getItem(uid);
        if (item == null) {
            return r.fail("物品不存在");
        }
        // 根据物品是否是唯一的来扣除
        if (item.getTpl(role.toPlayBuildContext()).unique() || item.unique()) {
            return removeItem(uid, sender, false);
        } else {
            return removeItem(item, num, sender, false);
        }
    }

    private GameResult<RewardShow> removeItem(BaseBagItem item, long num, MergeMsgSender sender, boolean triggerRemoveEvent) {
        GameResult<RewardShow> r = new GameResult<>();
        if (item.num > num) {
            item.addNum(-num);
            MergeMsgSender.handle(sender, new ItemNumResp(item.type).addChange(item.uid, item.num), role);
            onItemDecreased(item.tid, num);
            return r;
        } else if(item.num == num) {
            GameResult<RewardShow> removeRes = removeItem(item.uid, sender, triggerRemoveEvent);
            if (!removeRes.ok()) {
                return r.fail(removeRes);
            }
        } else {
            return removeItem(item.tid, num, sender, triggerRemoveEvent);
        }
        return r;
    }

    /** 移除背包中的指定物品 */
    @Override
    public GameResult<RewardShow> removeItem(String uid, MergeMsgSender sender) {
        return removeItem(uid, sender, true);
    }

    /** 移除背包中的指定物品 */
    private GameResult<RewardShow> removeItem(String uid, MergeMsgSender sender, boolean triggerRemoveEvent) {
        GameResult<RewardShow> r = new GameResult<>();
        Iterator<E> iterator = this.getList().iterator();
        while (iterator.hasNext()) {
            E item = iterator.next();
            if (StringUtils.equals(item.uid, uid)) {
                iterator.remove();
                if (triggerRemoveEvent) {
                    r.data = onItemRemoved(item);
                }
                MergeMsgSender.handle(sender, new ItemRemoveResp(item.uid, item.type),role);
                onItemDecreased(item.tid, item.num);
                return r.success("删除物品成功");
            }
        }
        return r.fail("物品不存在");
    }

    /** 删除或扣除 */
    @Override
    public GameResult<RewardShow> removeItem(int tid, long num, MergeMsgSender sender) {
        return removeItem(tid, num, sender, true);
    }

    /** 删除或扣除 */
    private GameResult<RewardShow> removeItem(int tid, long num, MergeMsgSender sender, boolean triggerRemoveEvent) {
        GameResult<RewardShow> r = new GameResult<>();
        if (num < 0) {
            return r.fail("非法数据");
        }
        long total = count(tid);
        if (num > total) {
            return r.fail("数量不够");
        }
        // 待移除的数量
        long numRm = num;
        ItemRemoveResp removeResp = null;
        RewardShow rs = new RewardShow();
        Iterator<E> iterator = this.getList().iterator();
        while (iterator.hasNext()) {
            E item = iterator.next();
            if (item.tid == tid) {
                if (item.moreThan(numRm)) {
                    item.addNum(-numRm);
                    numRm = 0;
                    MergeMsgSender.handle(sender, new ItemNumResp(item.type).addChange(item.uid, item.num),role);
                } else {
                    iterator.remove();
                    if (triggerRemoveEvent) {
                        rs.merge(onItemRemoved(item));
                    }
                    if(removeResp == null){
                        removeResp = new ItemRemoveResp(item.type);
                    }
                    removeResp.add(item.uid);
                    numRm -= item.num;
                }
            }
            if (numRm <= 0) {
                break;
            }
        }
        if(removeResp != null){
            MergeMsgSender.handle(sender,removeResp,role);
        }
        onItemDecreased(tid, num);
        r.data = rs;
        return r.success();
    }

    /** 根据唯一id来删除 */
    @Override
    public GameResult<RewardShow> removeItemByUid(
            String uid, long num, MergeMsgSender sender) {
        GameResult<RewardShow> r = new GameResult<>();
        BaseBagItem item = this.getItem(uid);
        if (item == null) {
            return r.fail("物品不存在");
        }
        // 根据物品是否是唯一的来扣除
        if (item.getTpl(role.toPlayBuildContext()).unique() || item.unique()) {
            return removeItem(uid, sender);
        } else {
            return removeItem(item, num, sender, true);
        }
    }

    /** 完全空的格子 */
    @Override
    public int gridEmpty() {
        return this.capacity - itemSize();
    }

    @Override
    public void forGmAddCapacity(){
        this.capacity++;
    }

    protected abstract ItemAddResp createItemAddMsg();

    protected abstract E createItem(int tid, long num);

    protected GameResult<Void> customItemCheck(int tid, long num){
        return new GameResult<>();
    }

    protected void onItemIncreased(int tid, long num){
        doStat(tid, num);
    }

    protected void onItemCreated(E e){

    }

    protected void onItemDecreased(int tid, long num){
        doStat(tid, -num);
    }

    protected RewardShow onItemRemoved(E e){
        return new RewardShow();
    }

    /** 做统计 */
    private void doStat(int tid, long num) {
        if (role == null) {
            return;
        }
        StatItemIOEntity po = new StatItemIOEntity();
        po.setItemType(getItemType());
        po.setItemTid(tid);
        //表字段不统一, 有中文和数字, 只能做兼容
        po.setItemName(getItemTpl(tid).getName());
        po.setItemNum(num);
        po.setItemNumEnd(count(tid));
        po.read(role);
        role.getGame().stat.itemRecord(role, po);

        if (getItemType() == GDObj.Type.NUM) {
            switch (tid) {
                case GDObj.Num.M1:
                    if (num > 0) {
                        role.getSystem().update(RoleSystem.GmOper.M1_GET, num);
                    } else {
                        role.getSystem().update(RoleSystem.GmOper.M1_COST, Math.abs(num));
                    }
                    break;
            }
        } else if (getItemType() == GDObj.Type.PROP && num > 0) {
            switch (tid) {
                case 207012:
                    role.getSystem().update(RoleSystem.GmOper.TICKET_GET, num);
                    break;
                case 207013:
                    role.getSystem().update(RoleSystem.GmOper.CRYSTAL_GET, num);
                    break;
                case 207019:
                    role.getSystem().update(RoleSystem.GmOper.FATE_CRYSTAL_GET, num);
                    break;
                case 207015:
                    role.getSystem().update(RoleSystem.GmOper.ELITE_TICKET_GET, num);
                    break;
                case 400092:
                    role.getSystem().update(RoleSystem.GmOper.NIUBI_TICKET, num);
                    break;
                default:
                    break;
            }
        }
    }

    protected abstract byte getItemType();
}
